#include "StdAfx.h"
#include "LuaInterface.h"
#include "CharplanDoc.h"
#include "resource.h"
#include "MainFrm.h"
#include "CharplanView.h"
#include "Charplan.h"

#include <string>
#include <list>

extern "C"
{
	#include <lua.h>
	#include <lualib.h>
	#include <lauxlib.h>
}

const char* cas_events[]={
    "ClassChanged",
    "LevelChanged",
    "StatsChanged",
    "RAChanged",
    "OptionsChanged",
    "SpecChanged",
    "ChampChanged",
    "RaceChanged"
};
const int cas_event_nof=sizeof(cas_events)/sizeof(cas_events[0]);

enum enum_events
{
    EVENT_CLASS_CHANGED = 0,
    EVENT_LEVEL_CHANGED = 1,
    EVENT_STATS_CHANGED = 2,
    EVENT_RA_CHANGED    = 3,
    EVENT_OPTIONS_CHANGED=4,
    EVENT_SPEC_CHANGED  = 5,
    EVENT_CHAMP_CHANGED = 6,
    EVENT_RACE_CHANGED  = 7,
};


CLuaInterface::CLuaInterface(CCharplanDoc* p_doc)
{
    mp_doc = p_doc;
    mp_state = NULL;
    mb_debug_mode = false;

    #ifdef _DEBUG
       mb_debug_mode = true;
    #endif

    Init();
}


CLuaInterface::~CLuaInterface(void)
{
    if (mp_state) lua_close(mp_state);
    mp_state = NULL;
}

const CStringArray& CLuaInterface::GetLog()
{
    return m_log;
};

void  CLuaInterface::Log(const CString& str)
{
    if (m_log.GetCount()>1000) m_log.RemoveAll();
    m_log.Add(str);

    if (IsInDebugMode())
    {
        // Inform lua window
        POSITION pos = mp_doc->GetFirstViewPosition();
        while (pos)
        {
            CView*w = mp_doc->GetNextView(pos);
            if (w && w->IsKindOf(RUNTIME_CLASS(CCharplanView)))
            {
                CAPage* p = ((CCharplanView*)w)->GetActivePageWnd();
                if (p)
                    p->PostMessage(WM_LUA_ERROR, 0, (LPARAM)mp_doc);
            }
        }
    }
}

bool    CLuaInterface::IsInDebugMode()
{
    return mb_debug_mode;
}


void CLuaInterface::DumpError()
{
    mb_debug_mode = true;

    // dump lua error
    if( lua_isstring(mp_state, -1 ) )
    {
        const char* msg = lua_tostring( mp_state, -1 );
        if (msg) 
        {
            Log(CString("Error: ")+msg);

            CMainFrame* pFrame =(CMainFrame*)AfxGetMainWnd();
            pFrame->SetStatusText(CString("(see page 'Stats'->'Lua') LUA-Error: ")+ msg );

        }
    }
    lua_pop(mp_state, 1);
}

bool  CLuaInterface::Init()
{
    if (mp_state)
    {
        lua_close(mp_state);
        m_log.Add("----restart----");
    }

    mp_state = luaL_newstate();
    if (!mp_state) return false;

    luaL_openlibs(mp_state);

    m_events.clear();
    m_events.resize(cas_event_nof);


    // Register function
    lua_register(mp_state, "Log", CLuaInterface::LuaLog);
    lua_register(mp_state, "Debug", CLuaInterface::LuaDebug);
    lua_register(mp_state, "RegisterEvent", CLuaInterface::LuaRegisterEvent);
    lua_register(mp_state, "RegisterAfter", CLuaInterface::LuaRegisterAfter);
    //lua_register(mp_state, "Unregister", CLuaInterface::LuaUnregister);


    lua_pushlightuserdata(mp_state, this);
    lua_setfield(mp_state, LUA_GLOBALSINDEX,"-lc");


    // load files
    DoAllFiles();

    return true;
}


void    CLuaInterface::DoAllFiles()
{
    // Find all scripts
    std::list <std::string> script_files;

    WIN32_FIND_DATA FindFileData;
    HANDLE hFind;

    std::string base_path = theApp.GetDataPath() + "calc/";
    
    
    hFind = FindFirstFile( (base_path+std::string("*.lua")).c_str(), &FindFileData);
    if (hFind == INVALID_HANDLE_VALUE) return;

    do
    {
        script_files.push_back(base_path+FindFileData.cFileName);
    } while (FindNextFile(hFind, &FindFileData));

    FindClose(hFind);

    script_files.sort();

    for ( std::list <std::string>::iterator it=script_files.begin();
            it!=script_files.end();++it)
    {
        Log(CString("Loading: ")+it->c_str());
        if (luaL_dofile(mp_state,it->c_str()))   DumpError();
    }

    Log("loading done");

}

std::string CLuaInterface::Eval(const char* code)
{
    std::string mc("return ");
    mc += code;

    if (luaL_dostring(mp_state, mc.c_str()))  
    {
        DumpError();
        return code;
    };

    int n = lua_gettop(mp_state);
    std::string str;
    for (int i = 1; i <= n; i++)
    {
        const char* ps = lua_tostring(mp_state,i);
        if (ps)  str += ps;
    }
    lua_pop(mp_state,n);

    return str;
}

void   CLuaInterface::Execute(const char* code)
{
    if (luaL_dostring(mp_state, code))   DumpError();
}

bool  CLuaInterface::Test(const char* code)
{
    std::string mc("return ");
    mc += code;

    if (luaL_dostring(mp_state, mc.c_str()))  
    {
        DumpError();
        return false;
    };

    bool res = false;
    if (lua_gettop(mp_state)>0) 
    {
        res = 0!=lua_toboolean(mp_state,1);
    }

    lua_pop(mp_state,lua_gettop(mp_state));

    return res;
}



CLuaInterface* CLuaInterface::GetInterface(lua_State *L)
{
    lua_getfield(L, LUA_GLOBALSINDEX, "-lc"); 
    void* p = lua_touserdata(L,-1);
    lua_pop(L,1);
    if (!p)
    {
        lua_pushstring(L, "internal error - illegal class data");
        lua_error(L);
    }
    return (CLuaInterface*)p;
}

// Log(text,...)
int CLuaInterface::LuaLog(lua_State *L)
{
    CLuaInterface* pc_lua = GetInterface(L);
    if (!pc_lua) return 0;

    int n = lua_gettop(L);    // number of arguments

    CString str;
    for (int i = 1; i <= n; i++)
    {
        str += lua_tostring(L,i);
    }

    pc_lua->Log(str);

    return 0;
}

// Debug()
int CLuaInterface::LuaDebug(lua_State *L)
{
    CLuaInterface* pc_lua = GetInterface(L);
    if (!pc_lua) return 0;

    // next log message will enable the window
    pc_lua->mb_debug_mode = true;

    return 0;
}

// RegisterEvent(fct, event, ...)
int CLuaInterface::LuaRegisterEvent(lua_State *L)
{
    CLuaInterface* pc_lua = GetInterface(L);
    if (!pc_lua) return 0;

    int n = lua_gettop(L);    /* number of arguments */
    if (n<2)
    {
        lua_pushstring(L, "RegisterEvent requires function and event argument");
        lua_error(L);
        return 0;
    }

    const char* p_fct = lua_tostring(L,1);

    // insert events
    for (int t=2;t<=n;++t)
    {
        unsigned e_id=-1;

         // find event
        if (lua_isnumber(L,t))   e_id = (unsigned)lua_tonumber(L,t);
        else
        if (lua_isstring(L,t))
        {
            const char* p_ev = lua_tostring(L,t);

            for (e_id=0;e_id<cas_event_nof;++e_id) if (_stricmp(p_ev,cas_events[e_id])==0) break;
        }


        if (e_id>=cas_event_nof)
        {
            const char* p_ev = lua_tostring(L,t);
            CString str = CString("Event: '") + p_ev + "' unkown";
            lua_pushstring(L, str);
            lua_error(L);
            continue;
        }

        // check if already registered
        std::vector<std::string>::iterator ip = pc_lua->m_events[e_id].begin();
        std::vector<std::string>::iterator ip_end = pc_lua->m_events[e_id].end();
        for (;ip!=ip_end;++ip) if (*ip == p_fct) break;
        if (ip==ip_end)
        {
            pc_lua->m_events[e_id].push_back(p_fct);
        }
    }

    
    return 0;
}

// RegisterAfter(fct, afterfct,...)
int CLuaInterface::LuaRegisterAfter(lua_State *L)
{
    CLuaInterface* pc_lua = GetInterface(L);
    if (!pc_lua) return 0;

    int n = lua_gettop(L);    /* number of arguments */
    if (n<2)
    {
        lua_pushstring(L, "RegisterEvent requires function and afterfunction argument");
        lua_error(L);
        return 0;
    }

    const char* p_fct = lua_tostring(L,1);

    // check all event
    for (int i=2;i<=n;++i)
    {
        const char* p_afterfct = lua_tostring(L,i);

        std::vector < std::vector <std::string> >::iterator iev = pc_lua->m_events.begin();
        for (;iev!=pc_lua->m_events.end();++iev)
        {
            std::vector<std::string>::iterator ip = iev->begin();
            for (;ip!=iev->end();++ip)
                if (*ip == p_afterfct)
                {
                    // check if already registered
                    std::vector<std::string>::iterator ie= iev->begin();
                    for (;ie!=iev->end();++ie) if (*ie == p_fct) break;
                    if (ie==iev->end())
                    {
                        iev->push_back(p_fct);
                    }
                    
                    break;
                }
        }
    }

    return 0;
}

// Unregister(function)
int CLuaInterface::LuaUnregister(lua_State *L)
{
    CLuaInterface* pc_lua = GetInterface(L);
    if (!pc_lua) return 0;

    ASSERT(false);
    pc_lua->Log("not implemented");

    return 0;
}


void    CLuaInterface::SetVar(const char* name, float val, int index /*= LUA_GLOBALSINDEX*/)
{
    lua_pushnumber(mp_state,val);
    lua_setfield(mp_state,index,name);
}

void    CLuaInterface::SetVar(const char* name, double val, int index /*LUA_GLOBALSINDEX*/)
{
    lua_pushnumber(mp_state,val);
    lua_setfield(mp_state,index,name);
}


void    CLuaInterface::SetVar(const char* name, int val, int index /*= LUA_GLOBALSINDEX*/)
{
    lua_pushinteger(mp_state,val);
    lua_setfield(mp_state,index,name);
}

void    CLuaInterface::SetVar(const char* name, bool val, int index /*= LUA_GLOBALSINDEX*/)
{
    lua_pushboolean(mp_state,val);
    lua_setfield(mp_state,index,name);
}

void    CLuaInterface::SetVar(const char* name, const std::string& val, int index)      
{
    SetVar(name,val.c_str(),index); 
}

void    CLuaInterface::SetVar(const char* name, const char* val, int index /*= LUA_GLOBALSINDEX*/)
{
    lua_pushstring(mp_state,val);
    lua_setfield(mp_state,index,name);
}

bool    CLuaInterface::GetVar(const char* name, int & val, int index /* = LUA_GLOBALSINDEX*/)
{
    lua_getfield(mp_state,index,name);
    bool res = 0!=lua_isnumber(mp_state,-1);
    val = lua_tointeger(mp_state,-1);
    lua_pop(mp_state,1);
    return res;
}

bool    CLuaInterface::GetVar(const char* name, double & val, int index /* = LUA_GLOBALSINDEX*/)
{
    lua_getfield(mp_state,index,name);
    bool res = 0!=lua_isnumber(mp_state,-1);
    val = lua_tonumber(mp_state,-1);
    lua_pop(mp_state,1);
    return res;
}

int    CLuaInterface::OpenTable(const char * str, int prealloc, int index)
{
    lua_getfield(mp_state, index, str);
    if (lua_istable(mp_state, -1))
    {
        return lua_gettop(mp_state);
    }
    lua_pop(mp_state,1);

    return CreateTable(str, prealloc, index);
}

int    CLuaInterface::OpenTable(int idx, int prealloc, int index)
{
    lua_pushnumber(mp_state,idx);
    lua_gettable(mp_state, index);
    if (lua_istable(mp_state, -1))
    {
        return lua_gettop(mp_state);
    }
    lua_pop(mp_state,1);

    return CreateTable(idx, prealloc, index);
}

int   CLuaInterface::CreateTable(const char * str, int prealloc, int index)
{
    lua_createtable(mp_state,0,prealloc);
    int idx = lua_gettop(mp_state);
    lua_pushvalue(mp_state,-1);
    lua_setfield(mp_state, index, str);
    return idx;
}

int   CLuaInterface::CreateTable(int idx, int prealloc, int index)
{
    lua_createtable(mp_state,0,prealloc);
    int newidx = lua_gettop(mp_state);
    lua_pushnumber(mp_state,idx);
    lua_pushvalue(mp_state,-2);
    lua_settable(mp_state, index);
    return newidx;
}

void  CLuaInterface::CloseTable()
{
    lua_pop(mp_state,1);
}

void  CLuaInterface::Event(unsigned id, bool execute)
{
    if (id < cas_event_nof)
    {
        //Log(CString("--Event:")+cas_events[id]);
        std::vector<std::string>::iterator ip = m_events[id].begin();
        for (;ip!=m_events[id].end();++ip)
        {
            std::vector<std::string>::iterator ie = m_events_queue.begin();
            for (;ie!=m_events_queue.end();++ie) if (*ie == *ip) break;

            if (ie == m_events_queue.end())
                m_events_queue.push_back(*ip);
        }
    }

    if (execute)
    {
        std::vector<std::string>::iterator ip = m_events_queue.begin();
        for (;ip!=m_events_queue.end();++ip)
        {
            CallLuaFunction(ip->c_str());
        }
        m_events_queue.clear();
    }
}

void  CLuaInterface::CallLuaFunction(const char* name)
{
    lua_pushstring(mp_state,name);
    lua_gettable(mp_state, LUA_GLOBALSINDEX);
    if (!lua_isfunction(mp_state,-1))
    {
        lua_pop(mp_state,1);
        lua_pushstring(mp_state, (std::string("function '") + name + std::string("' is unknown")).c_str());
        DumpError();
        return;
    }

    if (lua_pcall (mp_state,  0, 0, 0))   DumpError();
}

void CLuaInterface::OnClassChanged(bool sendevent /*= true*/)
{
    SetVar("language",gdbinterface.CurrentLanguage().MakeLower());

    int tc = OpenTable("class",10);
        SetVar("id", mp_doc->GetClassID() ,tc);
        SetVar("name",    gdbinterface.GetClassName(),tc);
        SetVar("is_list_caster", gdbinterface.IsClassListCaster(),tc);
    CloseTable();
    SetVar("rr_skill", (int)max(0, ((int)(mp_doc->GetRR()/10))));

    CheckAbilities();
    CallLuaFunction("HardCaps");
    CopyStats();

    Event(EVENT_CLASS_CHANGED, sendevent);
    Event(EVENT_STATS_CHANGED, sendevent);
}

void CLuaInterface::OnRaceChanged(bool sendevent /*= true*/)
{
    enum_race_id  race = mp_doc->GetRaceID();
    CString race_name = gdbinterface.GetRaceName(race);

    SetVar("race", race_name);
    CallLuaFunction("HardCaps");
    CopyStats();

    Event(EVENT_RACE_CHANGED, sendevent);
    Event(EVENT_STATS_CHANGED, sendevent);
}

void CLuaInterface::CheckAbilities()
{
    CStringArray names;
    IntArray  levels;
    IntArray  type;

    gdbinterface.GetListOfAbilities(names,levels,type);

    int n_evade_level = 0;
    bool b_can_equip_shield = false;
    bool b_can_spec_shield = false;
    bool b_has_tank_bonus = false;
    bool b_can_parry = false;
    bool b_can_spec_parry = false;

    /// @todo replace strings
    for (int t=0;t<names.GetCount();t++)
    {
        CString str_level;
        str_level.Format(_T("%i"),levels[t]);
        if (levels[t]>50) str_level.Empty();

        CString temp = names[t];
        temp.MakeLower();

        // special
        if (type[t]>0 && (type[t]&15)!=7)
        {
            // check 'Shield'
            if (temp.Left(6)=="shield" || temp.Left(6)=="schild")  b_can_equip_shield = true;
            else
            // check parry
            if (temp.Left(5)=="parie" || temp.Left(5)=="parry")    b_can_parry = true;
        }
        else
        {
            // count "evade"
            if (temp.Left(8)=="ausweich" || temp.Left(5)=="evade") n_evade_level++;
            else
            if (temp=="kampfnarben" || temp.Left(9)=="hitpoints")  b_has_tank_bonus = true;
            else
            if (temp.Left(5)=="parie"  || temp.Left(5)=="parry")  b_can_parry = true;
        }
    }


    // Check skill-ables
    gdbinterface.GetListOfStyleLines(names);

    for (int t=0;t<names.GetCount();t++)
    {
        if (names[t].Left(6).MakeLower()=="shield" || names[t].Left(6).MakeLower()=="schild")  b_can_spec_shield = true;
        else
        if (names[t].Left(5).MakeLower()=="parie" || names[t].Left(5).MakeLower()=="parry")    b_can_spec_parry = true;
    }


    int tc = OpenTable("class",10);
        SetVar("can_equip_shield",b_can_equip_shield,tc);
        SetVar("can_train_shield",b_can_spec_shield,tc);
        SetVar("has_tank_bonus",b_has_tank_bonus,tc);
        SetVar("can_parry",b_can_parry,tc);
        SetVar("can_train_parry",b_can_spec_parry,tc);
    CloseTable();

    tc = OpenTable("evade",5);
        SetVar("level",n_evade_level,tc);
    CloseTable();
}

void CLuaInterface::OnLevelChanged(bool sendevent /*= true*/)
{
    SetVar("level", mp_doc->GetLevel());
    SetVar("level_abs", (int)mp_doc->GetLevel());

    CallLuaFunction("HardCaps");
    CopyStats();

    Event(1,sendevent);
    Event(2,sendevent);
}

void CLuaInterface::OnStatsChanged(bool sendevent /*= true*/)
{
    CopyStats();
    Event(EVENT_STATS_CHANGED, sendevent);
}

void  CLuaInterface::CopyStats()
{
    const char stats_str[8][4]={"str","con","dex","qui","int","pie","emp","cha"};

    int t_stats = OpenTable("stats",8);
    int t_st[8];
    for (int t=0;t<8;++t) t_st[t] = OpenTable(stats_str[t],10,t_stats);

    int n_stats_sum[8];
    int n_stats[8];

    // stats_by_user
    ZeroMemory(n_stats_sum,sizeof(n_stats_sum));
	mp_doc->GetStatsModif(n_stats_sum);

    for (int t=0;t<8;++t)   SetVar("user", n_stats_sum[t],t_st[t]);


    // stats_by_race
    gdbinterface.GetStats(n_stats);
    for (int t=0;t<8;++t)
    {
        n_stats_sum[t] += n_stats[t];
        SetVar("race", n_stats[t], t_st[t]);
    }

    // stat-add by level
    int   n_level_stats[4]; 
    gdbinterface.GetStatsPrimary(n_level_stats);

    for (int t=0;t<8;++t)   SetVar("class",0,t_st[t]);

    int n_level = (int)mp_doc->GetLevel();
    if (n_level>5)
    {
        int pri = n_level-5;
        int sec = (n_level-4)/2;
        int ter = (n_level/3)-1;

        SetVar("class", pri ,t_st[n_level_stats[0]]);
        n_stats_sum[n_level_stats[0]] += pri;

        SetVar("class", sec, t_st[n_level_stats[1]]);
        n_stats_sum[n_level_stats[1]] += sec;

        SetVar("class", ter, t_st[n_level_stats[2]]);
        n_stats_sum[n_level_stats[2]] += ter;
    }

    // vampiir
    if ( mp_doc->GetClassID() == ECI_Vampiir && n_level > 5)
    {
        int l = n_level -5;

        // Patch 1.82 -> Vampiir self buffs will now scale in effectiveness from 50% to 100% between the levels 5 and 40. 
        double factor = 1;
        if (n_level<40)
        {
            // not 100% correct but okay
            factor = 0.5 + 0.5 * (n_level-5)/35;
            factor = 0.5 + l / 70;
        }

        SetVar("vampiir", int(3*l*factor), t_st[0]); // "Vampiir Strength"
        SetVar("vampiir", int(3*l*factor), t_st[1]); // "Vampiir Constitution"
        SetVar("vampiir", int(3*l*factor), t_st[2]); // "Vampiir Dexterity"
        SetVar("vampiir", int(2*l*factor), t_st[3]); // "Vampiir Quickness"
        for (int t=4;t<8;++t) SetVar("vampiir", 0,   t_st[t]);

        n_stats_sum[0] += int(3*l*factor);
        n_stats_sum[1] += int(3*l*factor);
        n_stats_sum[2] += int(3*l*factor);
        n_stats_sum[3] += int(2*l*factor);
    }

    // Sum 1
    for (int t=0;t<8;++t)
    {
        SetVar("sum_base", n_stats_sum[t], t_st[t]);
    }


	// Realm abilities
    int r;
    r = mp_doc->GetRRValue(ERA_AUG_STRENGHT);     SetVar("rr", r, t_st[0]); n_stats_sum[0] += r;
    r = mp_doc->GetRRValue(ERA_AUG_CONSTITUTION); SetVar("rr", r, t_st[1]); n_stats_sum[1] += r;
    r = mp_doc->GetRRValue(ERA_AUG_DEXTERITY);    SetVar("rr", r, t_st[2]); n_stats_sum[2] += r;
    r = mp_doc->GetRRValue(ERA_AUG_QUICKNESS);    SetVar("rr", r, t_st[3]); n_stats_sum[3] += r;
    for (int t=4;t<8;++t) SetVar("rr", 0,   t_st[t]);

    if (n_level_stats[3] != -1)
    {
        r = mp_doc->GetRRValue(ERA_AUG_ACUITY);
        SetVar("rr", r,   t_st[n_level_stats[3]]);
        n_stats_sum[n_level_stats[3]] += r;
	}


    
    // User option
    int t_caps = OpenTable("cap",8);
        int cap_item, cap_item_ovr, cap_buff_base, cap_buff_spec, cap_buff_spec_int;
        GetVar("item_bonus",cap_item,t_caps);
        GetVar("item_bonus_overcap",cap_item_ovr,t_caps);
        GetVar("buff_bonus_base",cap_buff_base,t_caps);
        GetVar("buff_bonus_spec",cap_buff_spec,t_caps);
        GetVar("buff_bonus_spec_int",cap_buff_spec_int,t_caps);
    CloseTable();

    CString temp;
    int v;
    for (int t=0;t<8;t++)
	{
		temp.Format(_T("BI_%i"),t);
        v = mp_doc->GetOption(temp,cap_item);
        v = CLAMP(v,0,cap_item);
        SetVar("item", v ,t_st[t]);
        n_stats_sum[t] += v;

		temp.Format(_T("BICAP_%i"),t);
        v = mp_doc->GetOption(temp,cap_item_ovr);
        v = CLAMP(v,0,cap_item_ovr);
        SetVar("item_overcap", v ,t_st[t]);
        n_stats_sum[t] += v;
    }

    for (int t=0;t<3;t++)
	{
        temp.Format(_T("BB_%i"),t);
        v = mp_doc->GetOption(temp,cap_buff_base);
        v = CLAMP(v,0,cap_buff_base);
        SetVar("buff_base", v ,t_st[t]);
        n_stats_sum[t] += v;
    }
    for (int t=3;t<8;t++) SetVar("buff_base", 0 ,t_st[t]);


    for (int t=0;t<4;t++)
	{
		temp.Format(_T("BBS_%i"),t);
        v = mp_doc->GetOption(temp,cap_buff_spec);
        v = CLAMP(v,0,cap_buff_spec);
        SetVar("buff_spec", v ,t_st[t]);
        n_stats_sum[t] += v;
    }
    for (int t=4;t<8;t++) SetVar("buff_spec", 0 ,t_st[t]);
        
    if (gdbinterface.IsClassListCaster())
	{
        v = mp_doc->GetOption(temp,cap_buff_spec_int);
        v = CLAMP(v,0,cap_buff_spec_int);
        SetVar("buff_spec", v ,t_st[4]);
        n_stats_sum[4] += v;
	}


    // mythical
    for (int t=0;t<8;++t) SetVar("mythical", 0,   t_st[t]);
    for (int t=0;t<8;++t) SetVar("mythical_overcap", 0,   t_st[t]);

	int	mytic_item_buff = mp_doc->GetOption(_T("MYTIC_ITEM"),-1);
	int	mytic_item_value = mp_doc->GetOption(_T("MI_VAL"),0);
	int	mytic_item_cap = mp_doc->GetOption(_T("MI_VAL_CAP"),0);

	if (mytic_item_buff>=0 && (mytic_item_value>0 || mytic_item_cap>0))
	{
        SetVar("mythical", mytic_item_value,       t_st[mytic_item_buff]);
        SetVar("mythical_overcap", mytic_item_cap, t_st[mytic_item_buff]);
        n_stats_sum[mytic_item_buff] += mytic_item_value+mytic_item_cap;
	}


    // Sum 2
    for (int t=0;t<8;++t)
    {
        SetVar("total", n_stats_sum[t], t_st[t]);
    }


    for (int t=0;t<8;++t) CloseTable();
    CloseTable(); // stats
}

void CLuaInterface::OnRAChanged(bool sendevent /*= true*/)
{
    // get data
	IntArray output;
	gdbinterface.GetListOfRANF(output);

    int t_ras = CreateTable("ra",output.GetCount()+1);


    int skill;
    int value;

    CString temp;

	for (int t=0;t<output.GetCount();t++)
	{
		gdbinterface.SelectRANF(output[t]);
        CString name = gdbinterface.GetRANFName();

        skill = mp_doc->GetRRNFLevel((enum_RA_id) output[t]);
        value = 0;
        if (skill>0) value = gdbinterface.GetRANFStatModif(output[t],skill);

        int t_thisra = OpenTable(output[t],5,t_ras);
            SetVar("name", name, t_thisra);
            SetVar("skill", skill, t_thisra);
            SetVar("value", value, t_thisra);
        CloseTable();
	}

    int rr = mp_doc->GetRR();
    SetVar("points", rr, t_ras);

    temp.Format(_T("R%iL%i"),1+rr/10,rr%10);
    SetVar("rank",temp, t_ras);

    SetVar("skill",(int)(rr/10), t_ras);

    CloseTable();

    Event(3,sendevent);
}

void CLuaInterface::OnUserOptionChanged(bool sendevent /*= true*/)
{
    int item_cap,hp_cap,hp_over_cap;
    int tc = OpenTable("cap",8);
        GetVar("item_bonus", item_cap, tc);
        GetVar("hp_bonus", hp_cap, tc);
        GetVar("hp_bonus_overcap", hp_over_cap, tc);
    CloseTable();

    
    SetVar("USE_ALTERNATIVE_DEX", 0!=GETOPTIONDEF("ALTERNATIVE_DEX",0));

    int parry_items = mp_doc->GetOption(_T("BI_10"),item_cap);
    int block_items = mp_doc->GetOption(_T("BI_11"),item_cap);
    parry_items = CLAMP(parry_items,0,item_cap);
    block_items = CLAMP(block_items,0,item_cap);

    tc = OpenTable("parry",5);
        SetVar("items", parry_items,tc);
    CloseTable();

    tc = OpenTable("block",5);
        SetVar("items", block_items,tc);
    CloseTable();

    int hp_items  = mp_doc->GetOption(_T("BI_8"),hp_cap);
    int hp_items_overcap = mp_doc->GetOption(_T("BICAP_8"),hp_over_cap);
    hp_items = CLAMP(hp_items,0,hp_cap);
    hp_items_overcap = CLAMP(hp_items_overcap,0,hp_over_cap);
    tc = OpenTable("hitpoints",5);
        SetVar("items", hp_items, tc);
        SetVar("items_overcap", hp_items_overcap, tc);
    CloseTable();

    Event(4,sendevent);
}

void CLuaInterface::OnSpecChanged(bool sendevent /*= true*/)
{
    int tc = CreateTable("skills");

        CStringArray output;
        gdbinterface.GetListOfStyleLines(output);

        for (int t=0;t<output.GetSize();++t)
        {
            SetVar(output[t],mp_doc->GetSpecLevel(output[t]), tc);
        }


        gdbinterface.GetListOfSpellLines(output);

        for (int t=0;t<output.GetSize();++t)
        {
            SetVar(output[t],mp_doc->GetSpecLevel(output[t]), tc);
        }

    CloseTable();

    Event(EVENT_SPEC_CHANGED, sendevent);
}

void CLuaInterface::OnChampChanged(bool sendevent /*= true*/)
{
    int tc = OpenTable("champ",5);
        SetVar("level", mp_doc->GetCHlevel(),tc);
    CloseTable();

    Event(6,sendevent);
}